﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;



namespace BlackStar
{
    public class TextboxContentReader : ContentTypeReader<Textbox>
    {
        protected override Textbox Read(ContentReader input, Textbox existingInstance)
        {
            Textbox result = new Textbox();
            result.ReadContent(input);
            return result;
        }
    }

    public class Textbox : Label
    {
        protected Color myHighlightBack = Color.Navy;
        protected Color myHighlightFore = Color.White;

        protected char myPasswordChar = char.MinValue;

        protected int mySelectStart = 0;
        protected int mySelectEnd = 0;
        protected int displayStart = 0;

        protected bool cursorBlink = true;
        protected int cursorTime = 0;
        protected bool selecting = false;

        protected bool myLocked = false;


        protected internal override void ReadContent(ContentReader input)
        {
            base.ReadContent(input);
            myHighlightBack = input.ReadColor();
            myHighlightFore = input.ReadColor();
            myPasswordChar = input.ReadChar();
        }

        public override void Draw()
        {
            if (!myVisible) return;

            int ss, se;

            gui.theme.Textbox.Draw("Default", myDrawRectangle, this.Color);

            // get the text that is currently visible.
            string currentText;
            float cursorpos;

            currentText = DisplayText();

            ss = Math.Min(Math.Max(mySelectStart - displayStart, 0), currentText.Length);
            se = Math.Min(Math.Max(mySelectEnd - displayStart, 0), currentText.Length);

            cursorpos = gui.theme.Fonts[this.Font].CursorPos(se, currentText, this.FontSize);
            while (cursorpos > myInterior.Width && myInterior.Width > 0)
            {
                displayStart += 1;
                ss = Math.Max(mySelectStart - displayStart, 0);
                se = Math.Max(mySelectEnd - displayStart, 0);
                currentText = DisplayText();
                cursorpos = gui.theme.Fonts[this.Font].CursorPos(se, currentText, this.FontSize);
            }

            if (this.HasFocus() && (cursorBlink || cursorTime > 0))
            {
                // draw the cursor.
                gui.theme.Fonts[this.Font].DrawCursor(se, currentText, myInterior, this.FontColor, this.FontSize);
            }

            gui.theme.Fonts[this.Font].Draw(currentText, myInterior, this.FontSize, this.FontColor);

            if (se != ss)
            {
                // draw the highlight.
                Vector2 startV = myGui.theme.Fonts[this.Font].MeasureString(this.FontSize, currentText.Substring(0, ss));
                Vector2 endV = myGui.theme.Fonts[this.Font].MeasureString(this.FontSize, currentText.Substring(0, se));
                string highlightText = currentText.Substring(Math.Min(ss, se), Math.Abs(se - ss));

                Rectangle highlightArea = new Rectangle(myInterior.X + (int)Math.Ceiling(Math.Min(startV.X, endV.X)),
                                                        myInterior.Y,
                                                        (int)Math.Ceiling(Math.Abs((endV.X - startV.X))),
                                                        (int)Math.Max(endV.Y, startV.Y));

                highlightArea.Width -= Math.Max(highlightArea.Right - myInterior.Right, 0);
                //if (highlightArea.Right > interior.Right) highlightArea.Width -= 
                gui.DrawRectangle(highlightArea, this.myHighlightBack, true);

                // Now Redraw the text highlighted
                // we add one to the highlight area width, because sometimes the expected text doesn't quite fit.
                highlightArea.Width += 1;
                gui.theme.Fonts[this.Font].Draw(highlightText, highlightArea, this.FontSize, myHighlightFore);
            }

            // we call this last because child components should be drawn on top of this.
            DrawComponents();
        }

        protected string DisplayText()
        {
            if (myText.Trim() == string.Empty) return string.Empty;

            string disp = myText.Substring(displayStart);
            return (myPasswordChar > char.MinValue) ? new string(myPasswordChar, disp.Length) : disp;
        }

        protected string SelectedText()
        {
            return myText.Substring(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
        }

        public override void Update(GameTime gameTime)
        {
            this.cursorBlink = ((gameTime.TotalRealTime.Milliseconds % 1000) >= 500);
            cursorTime = Math.Max(0, cursorTime - gameTime.ElapsedGameTime.Milliseconds);

            if (selecting && gui.mouse.LeftButton)
            {
                mySelectEnd = myGui.theme.Fonts[this.Font].CursorPos(this.myText, gui.mouse.Location, myInterior, this.FontSize) + displayStart;

                if (mySelectEnd < displayStart) displayStart = mySelectEnd;
                while (mySelectEnd < 0) { mySelectEnd++; displayStart--; }
                displayStart = Math.Max(displayStart, 0);

                mySelectEnd = Math.Min(mySelectEnd, myText.Length);
                cursorTime = 500;
            }

            // if the left button is no longer down, stop selecting.
            if (!gui.mouse.LeftButton) { selecting = false; }


            base.Update(gameTime);
        }

        public override void DoMouseDown(MouseEventArgs e)
        {
            // take focus
            this.TakeFocus();
            if (e.Buttons.Left && myText.Length > 0)
            {
                mySelectEnd = mySelectStart = myGui.theme.Fonts[this.Font].CursorPos(this.Text, e.Location, myInterior, this.FontSize) + displayStart;
                cursorTime = 500;
                selecting = true;
            }
        }

        public override void DoKeyDown(KeyEventArgs e)
        {
            string newText = myText;

            switch (e.Key)
            {
                case Keys.Tab:
                    if (myGui.keyboard.Shift)
                    { DoTabPrev(); }
                    else
                    { DoTabNext(); }
                    break;
                case Keys.Home:
                    mySelectEnd = 0;
                    displayStart = 0;
                    if (!myGui.keyboard.Shift) mySelectStart = mySelectEnd;
                    break;
                case Keys.End:
                    mySelectEnd = myText.Length;
                    if (!myGui.keyboard.Shift) mySelectStart = mySelectEnd;
                    break;
                case Keys.Left:
                    mySelectEnd -= 1;
                    mySelectEnd = Math.Max(mySelectEnd, 0);
                    if (!myGui.keyboard.Shift) { mySelectStart = mySelectEnd; }
                    break;
                case Keys.Right:
                    mySelectEnd += 1;
                    mySelectEnd = Math.Min(mySelectEnd, myText.Length);
                    if (!myGui.keyboard.Shift) { mySelectStart = mySelectEnd; }
                    break;
                case Keys.Back:
                    if (myLocked) break;
                    if (mySelectEnd != mySelectStart)
                    {
                        newText = myText.Remove(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
                        mySelectStart = mySelectEnd = Math.Min(mySelectStart, mySelectEnd);
                    }
                    else if (mySelectStart > 0)
                    {
                        newText = myText.Remove(mySelectStart - 1, 1);
                        mySelectStart = mySelectEnd -= 1;
                    }
                    break;
                case Keys.Delete:
                    if (myLocked) break;
                    if (mySelectEnd != mySelectStart)
                    {
                        newText = myText.Remove(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
                        mySelectStart = mySelectEnd = Math.Min(mySelectStart, mySelectEnd);
                    }
                    else
                    {
                        if (mySelectStart < myText.Length)
                        { newText = Text.Remove(mySelectStart, 1); }
                    }
                    break;
                default:
                    // convert it to keystroke?
                    if (myGui.keyboard.Control)
                    {
                        switch (e.Key)
                        {
                            case Keys.C:
                                // copy
                                if (myPasswordChar > char.MinValue) break;
                                myGui.clipboard.Text = this.SelectedText();
                                break;
                            case Keys.V:
                                // paste
                                if (myLocked) break;
                                newText = myText.Remove(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
                                mySelectStart = mySelectEnd = Math.Min(mySelectStart, mySelectEnd);
                                string cbtext = myGui.clipboard.Text;
                                newText = newText.Insert(mySelectEnd, cbtext);
                                mySelectStart = mySelectEnd += cbtext.Length;
                                break;
                            case Keys.X:
                                // cut
                                if (myPasswordChar > char.MinValue) break;
                                myGui.clipboard.Text = this.SelectedText();
                                if (myLocked) break;
                                newText = myText.Remove(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
                                mySelectStart = mySelectEnd = Math.Min(mySelectStart, mySelectEnd);
                                break;
                            case Keys.A:
                                // select all
                                mySelectStart = 0;
                                mySelectEnd = myText.Length;
                                break;
                        }
                    }
                    else
                    {
                        string aKey = myGui.keyboard.GetChar(e.Key);
                        if (aKey == "") break;
                        newText = myText.Remove(Math.Min(mySelectStart, mySelectEnd), Math.Abs(mySelectEnd - mySelectStart));
                        mySelectStart = mySelectEnd = Math.Min(mySelectStart, mySelectEnd);
                        newText = newText.Insert(mySelectEnd, aKey);
                        mySelectStart = mySelectEnd += 1;
                    }
                    break;
            }
            cursorTime = 500;

            ScrollToCursor();

            // we set the text property instead of the private myText
            // so that the Text changed event will fire.
            this.Text = newText;

            OnKeyDown(e);
        }

        [ContentSerializer(Optional = true)]
        public Color HighlightForeColor
        {
            get { return myHighlightFore; }
            set { myHighlightFore = value; }
        }

        [ContentSerializer(Optional = true)]
        public Color HighlightBackColor
        {
            get { return myHighlightBack; }
            set { myHighlightBack = value; }
        }

        [ContentSerializer(Optional = true)]
        public bool Locked
        {
            get { return myLocked; }
            set { myLocked = value; }
        }

        [ContentSerializer(Optional = true)]
        public char PasswordChar
        {
            get { return myPasswordChar; }
            set { myPasswordChar = value; }
        }

        [ContentSerializer(Optional = true)]
        public int SelectEnd
        {
            get { return mySelectEnd; }
            set { mySelectEnd = value; ScrollToCursor(); }
        }

        [ContentSerializer(Optional = true)]
        public int SelectStart
        {
            get { return mySelectStart; }
            set { mySelectStart = value; ScrollToCursor(); }
        }

        protected void ScrollToCursor()
        {
            while (mySelectEnd < displayStart)
            { displayStart -= 1; mySelectEnd += 1; mySelectStart += 1; }
        }


        public override void DoKeyUp(KeyEventArgs e)
        {
            OnKeyUp(e);
        }

        protected override void SetText(string value)
        {
            mySelectStart = Math.Min(mySelectStart, value.Length);
            mySelectEnd = Math.Min(mySelectEnd, value.Length);
            displayStart = Math.Min(displayStart, value.Length);

            base.SetText(value);
        }
    }
}
